package com.xy.tool.minio.util;


import com.xy.tool.minio.configuration.MinioProp;
import com.xy.tool.minio.domain.resp.FileUploadResponse;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.util.*;

@Slf4j
@Component
public class MinioUtil {

    @Autowired
    private MinioProp minioProp;

    @Autowired
    private MinioClient client;

    public static String storageBucket;

    @Value("${storage-bucket}")
    public void setStorageBucket(String storageBucket) {
        MinioUtil.storageBucket = storageBucket;
    }

    public static String templateBucket;

    @Value("${ledger_bucket}")
    public void setTemplateBucket(String templateBucket) {
        MinioUtil.templateBucket = templateBucket;
    }

    public static String tmpBucket;

    @Value("${tmp-bucket}")
    public void setTmpBucket(String tmpBucket) {
        MinioUtil.tmpBucket = tmpBucket;
    }

    public static String userHeadImage;

    @Value("${userHeadImage-bucket}")
    public void setUserHeadImage(String userHeadImage) {
        MinioUtil.userHeadImage = userHeadImage;
    }

    public static String qrCodeImage;

    @Value("${qrcode-bucket}")
    public void setQrCodeImage(String qrCodeImage) {
        MinioUtil.qrCodeImage = qrCodeImage;
    }

    /**
     * 创建bucket
     */
    public void createBucket(String bucketName) throws Exception {
        if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
            client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 上传文件
     */
    public FileUploadResponse uploadFile(MultipartFile file, String bucketName) throws Exception {
        //判断文件是否为空
        if (null == file || 0 == file.getSize()) {
            return null;
        }
        //判断存储桶是否存在  不存在则创建
        createBucket(bucketName);
        //文件名
        String originalFilename = file.getOriginalFilename();
        //新的文件名 = 存储桶文件名_时间戳.后缀名
        assert originalFilename != null;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String fileName = bucketName + "_" +
                System.currentTimeMillis() + "_" + format.format(new Date()) + "_" + new Random().nextInt(1000) +
                originalFilename.substring(originalFilename.lastIndexOf("."));
        //开始上传
        client.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(
                                file.getInputStream(), file.getSize(), -1)
                        .contentType(file.getContentType())
                        .build());
        String url = minioProp.getEndpoint() + "/" + bucketName + "/" + fileName;
        String urlHost = minioProp.getFilHost() + "/" + bucketName + "/" + fileName;
        return new FileUploadResponse(url, urlHost, fileName);
    }


    /**
     * 上传新建文件
     */
    public Long uploadFile(InputStream file, String fileName, String bucketName) throws Exception {
        //判断文件是否为空
        ByteArrayOutputStream baos = StreamUtils.cloneInputStream(file);
        long length = StreamUtils.readBytes(new ByteArrayInputStream(baos.toByteArray())).length;
        if (file != null && length > 0) {
            //判断存储桶是否存在  不存在则创建
            createBucket(bucketName);
            //开始上传
            client.putObject(
                    PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(
                                    new ByteArrayInputStream(baos.toByteArray()), length, -1)
                            .build());
        } else {
            throw new MinioException("文件为空");
        }
        return length;
    }


    /**
     * 获取全部bucket
     *
     * @return
     */
    public List<Bucket> getAllBuckets() throws Exception {
        return client.listBuckets();
    }

    /**
     * 根据bucketName获取信息
     *
     * @param bucketName bucket名称
     */
    public Optional<Bucket> getBucket(String bucketName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, InternalException, ErrorResponseException, ServerException, XmlParserException {
        return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
    }

    /**
     * 根据bucketName删除信息
     *
     * @param bucketName bucket名称
     */
    public void removeBucket(String bucketName) throws Exception {
        client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 获取⽂件外链
     *
     * @param bucketName bucket名称
     * @param objectName ⽂件名称
     * @param expires    过期时间 <=7
     * @return url
     */
    public String getObjectURL(String bucketName, String objectName, Integer expires) throws Exception {
        return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).method(Method.GET).build());
    }

    /**
     * 获取⽂件
     *
     * @param bucketName bucket名称
     * @param objectName ⽂件名称
     * @return ⼆进制流
     */
    public InputStream getObject(String bucketName, String objectName) throws Exception {

        return client.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).extraHeaders(new HashMap<>()).build());
    }

    /**
     * 上传⽂件
     *
     * @param bucketName bucket名称
     * @param objectName ⽂件名称
     * @param stream     ⽂件流
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
     */
    public void putObject(String bucketName, String objectName, InputStream stream) throws
            Exception {
        client.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, stream.available(), -1).contentType(objectName.substring(objectName.lastIndexOf("."))).build());
    }

    /**
     * 上传⽂件
     *
     * @param bucketName  bucket名称
     * @param objectName  ⽂件名称
     * @param stream      ⽂件流
     * @param size        ⼤⼩
     * @param contextType 类型
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
     */
    public void putObject(String bucketName, String objectName, InputStream stream, long
            size, String contextType) throws Exception {
        client.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, size, -1).contentType(contextType).build());
    }

    /**
     * 获取⽂件信息
     *
     * @param bucketName bucket名称
     * @param objectName ⽂件名称
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject
     */
    public StatObjectResponse getObjectInfo(String bucketName, String objectName) throws Exception {
        return client.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 删除⽂件
     *
     * @param bucketName bucket名称
     * @param objectName ⽂件名称
     * @throws Exception https://docs.minio.io/cn/java-client-apireference.html#removeObject
     */
    public void removeObject(String bucketName, String objectName) throws Exception {
        client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 获取某bucket中的全部对象
     *
     * @param bucketName bucket名称
     * @throws Exception https://docs.minio.io/cn/java-client-apireference.html#listObject
     */
    public void listObjects(String bucketName) {
        try {
            // 检查'mybucket'是否存在。
            boolean found = client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (found) {
                // 列出'my-bucketname'里的对象
                Iterable<Result<Item>> myObjects = client.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
                for (Result<Item> result : myObjects) {
                    Item item = result.get();
                    System.out.println("objName:" + item.objectName());
                    System.out.println("etag:" + item.etag());
                    System.out.println("storageClass:" + item.storageClass());
                    System.out.println("versionId:" + item.versionId());
                    System.out.println("lastModified:" + item.lastModified());
                    System.out.println("size:" + item.size());
                    System.out.println("isDir:" + item.isDir());
                    System.out.println("isLatest:" + item.isLatest());
                    System.out.println("owner:" + item.owner());
                    System.out.println("userMetadata:" + item.userMetadata());
                }
            } else {
                System.out.println("mybucket does not exist");
            }
        } catch (MinioException e) {
            System.out.println("Error occurred: " + e);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
    }

    /**
     * 判断bucket是否存在
     * @param bucketName
     * @return
     */
    public boolean fileExists(String bucketName, String fileName) {
        // 检查'mybucket'是否存在。
        try {
            boolean foundBucket = client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (foundBucket) {
                client.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
                return true;
            } else {
                throw new MinioException("bucket：" + bucketName + "不存在");
            }
        } catch (ErrorResponseException e) {
            return false;
        } catch (Exception e) {
            log.error("判断bucket:" + bucketName + "是否存在失败");
        }
        return false;
    }

    /**
     * 获取文件最后修改时间
     * @param bucketName
     * @param fileName
     * @return 时间戳 10位（秒）
     */
    public Long lastModified(String bucketName, String fileName) {
        StatObjectResponse stat;
        long modifySecond;
        try {
            stat = client.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
            modifySecond = stat.lastModified().withZoneSameInstant(ZoneId.of("Asia/Shanghai")).toEpochSecond();
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException("获取文件最后修改时间失败！");
        }
        return modifySecond;
    }

    /**
     * 判断是否有以某个前缀开头的文件（判断文件夹）
     * @param bucketName
     * @param folder
     * @return
     */
    public Boolean existsFolder(String bucketName, String folder) {
        try {
            Iterable<Result<Item>> results = client.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(folder).build());
            return results.iterator().hasNext();
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException("判断是否存在特定前缀：" + folder + "失败！");
        }
    }

    /**
     * 判断历史版本号
     * @param bucketName
     * @param folder
     * @return
     */
    public Integer fileVersion(String bucketName, String folder) {
        Integer ver = 0;
        try {
            Iterable<Result<Item>> results = client.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(folder).recursive(true).build());
            for (Result<Item> result : results) {
                String objectName = result.get().objectName();
                String substring = objectName.substring(0, objectName.lastIndexOf("/"));
                String numStr = substring.substring(substring.lastIndexOf("/") + 1);
                if (StringUtils.isNumeric(numStr)) {
                    Integer curVer = Integer.valueOf(numStr);
                    if (curVer > ver) {
                        ver = curVer;
                    }
                }

            }
            return ver;
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException("判断历史版本号失败！");
        }
    }


    /**
     * 查询某个路径下minio文件
     * @param bucketName
     * @param folder
     * @return
     */
    public List<String> getFiles(String bucketName, String folder) {
        try {
            Iterable<Result<Item>> results = client.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(folder).recursive(true).build());
            ArrayList<String> fileNames = new ArrayList<>();
            for (Result<Item> result : results) {
//                if (!result.get().isDir()) {
//                    String objectName = result.get().objectName();
//                    fileNames.add(objectName);
//                }
                if (result.get().objectName().split("/").length == 2) {
                    fileNames.add(result.get().objectName());
                }
            }
            return fileNames;
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException("查询" + bucketName + "下所有文件名失败！");
        }
    }


}
